package com.soulkun.smarthome.ui.service

import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.soulkun.smarthome.logic.Constant
import com.soulkun.smarthome.logic.dto.AlertMessageDTO
import com.soulkun.smarthome.logic.dto.FamilyMessageDTO
import com.soulkun.smarthome.logic.dto.StatusMessageDTO
import com.soulkun.smarthome.logic.dto.UserMessageDTO
import com.soulkun.smarthome.logic.enum.MessageCodeEnum
import com.soulkun.smarthome.logic.manager.BroadcastManager
import com.soulkun.smarthome.logic.model.MessageModel
import kotlinx.coroutines.*
import okhttp3.*
import okio.ByteString
import soulkun.library.helper.sharedpreferences.SharedPreferencesHelper
import soulkun.library.utils.getAppContext
import soulkun.library.utils.logE
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class WebSocketService : Service() {

    companion object {
        private const val HEART_BEAT_RATE = 60 * 1000L
        private const val FAILURE_RECONNECT_RATE = 60 * 1000L
    }

    private val sharedPreferences by lazy {
        SharedPreferencesHelper(getAppContext().packageName)
    }
    private val gson = GsonBuilder().setDateFormat("yyyy-MM-dd hh:mm:ss").create()

    @Volatile
    private var webSocket: WebSocket? = null

    @Volatile
    private var heartBeatCheckJob: Job? = null

    @Volatile
    private var reconnectJob: Job? = null

    private lateinit var coroutineDispatcher: ExecutorCoroutineDispatcher

    override fun onCreate() {
        super.onCreate()
        coroutineDispatcher = Executors.newFixedThreadPool(3).asCoroutineDispatcher()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        initWebSocket()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onUnbind(intent: Intent?): Boolean {
        return super.onUnbind(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        heartBeatCheckJob?.cancel()
        heartBeatCheckJob = null
        reconnectJob?.cancel()
        reconnectJob = null
        coroutineDispatcher.cancel()
        coroutineDispatcher.close()
        webSocket?.close(1001, "主动关闭连接")
        webSocket = null
    }

    private fun initWebSocket() {
        val userId = sharedPreferences.getLong(Constant.SHARED_PREFERENCE_USER_ID)
        val okHttpClient = OkHttpClient.Builder().readTimeout(10, TimeUnit.SECONDS).build()
        val request = if (System.currentTimeMillis() < sharedPreferences.getLong(Constant.SHARED_PREFERENCE_ACCESS_TIME) - 1000 * 60) {
            Request.Builder()
                .url("${Constant.BASE_URL_WEBSOCKET}/$userId")
                .addHeader(
                    "Authorization",
                    sharedPreferences.getString(Constant.SHARED_PREFERENCE_ACCESS_TOKEN)
                )
                .build()
        }else{
            Request.Builder()
                .url("${Constant.BASE_URL_WEBSOCKET}/$userId")
                .addHeader(
                    "Authorization",
                    sharedPreferences.getString(Constant.SHARED_PREFERENCE_ACCESS_TOKEN)
                )
                .addHeader(
                    "Refresh",
                    sharedPreferences.getString(Constant.SHARED_PREFERENCE_REFRESH_TOKEN)
                )
                .build()
        }
        GlobalScope.launch(coroutineDispatcher) {
            okHttpClient.newWebSocket(request, object : WebSocketListener() {
                override fun onOpen(webSocket: WebSocket, response: Response) {
                    super.onOpen(webSocket, response)
                    val accessToken = response.header("Authorization")
                    val accessTime = response.header("Access-Time")
                    if (accessToken != null && accessTime != null) {
                        sharedPreferences.putString(Constant.SHARED_PREFERENCE_ACCESS_TOKEN, accessToken)
                        sharedPreferences.putLong(Constant.SHARED_PREFERENCE_ACCESS_TIME, accessTime.toLongOrNull() ?: 0L)
                    }
                    this@WebSocketService.webSocket = webSocket
                    heartBeatCheckJob = GlobalScope.launch(coroutineDispatcher) {
                        heartBeatCheck()
                    }
                    "建立".logE()
                    reconnectJob?.cancel()
                    reconnectJob = null
                }

                override fun onMessage(webSocket: WebSocket, text: String) {
                    super.onMessage(webSocket, text)
                    val messageModel = gson.fromJson<MessageModel<Any>>(text, object: TypeToken<MessageModel<Any>>(){}.type )
                    val messageData = gson.toJson(messageModel.messageData)
                    when (messageModel.messageCode) {
                        MessageCodeEnum.Status.code -> {
                            val statusMessage = gson.fromJson(
                                messageData,
                                StatusMessageDTO::class.java
                            )
                            BroadcastManager.sendBroadcast(
                                this@WebSocketService,
                                MessageCodeEnum.Status,
                                statusMessage
                            )
                        }
                        MessageCodeEnum.Alert.code -> {
                            val alertMessage = gson.fromJson(
                                messageData,
                                AlertMessageDTO::class.java
                            )
                            BroadcastManager.sendBroadcast(
                                this@WebSocketService,
                                MessageCodeEnum.Alert,
                                alertMessage
                            )
                        }
                        MessageCodeEnum.Family.code -> {
                            val familyMessage = gson.fromJson(
                                messageData,
                                FamilyMessageDTO::class.java
                            )
                            BroadcastManager.sendBroadcast(
                                this@WebSocketService,
                                MessageCodeEnum.Family,
                                familyMessage
                            )
                        }
                        MessageCodeEnum.User.code -> {
                            val userMessage = gson.fromJson(
                                messageData,
                                UserMessageDTO::class.java
                            )
                            BroadcastManager.sendBroadcast(
                                this@WebSocketService,
                                MessageCodeEnum.User,
                                userMessage
                            )
                        }
                    }
                }

                override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
                    super.onMessage(webSocket, bytes)
                }

                override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
                    super.onClosing(webSocket, code, reason)
                    heartBeatCheckJob?.cancel()
                    heartBeatCheckJob = null
                    reconnectJob?.cancel()
                    reconnectJob = null
                    this@WebSocketService.webSocket = null
                }

                override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
                    super.onClosed(webSocket, code, reason)
                    heartBeatCheckJob?.cancel()
                    heartBeatCheckJob = null
                    reconnectJob?.cancel()
                    reconnectJob = null
                    this@WebSocketService.webSocket = null
                }

                override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                    super.onFailure(webSocket, t, response)
                    "失败".logE()
                    t.printStackTrace()
                    if (reconnectJob == null) {
                        reconnectJob = GlobalScope.launch(coroutineDispatcher) {
                            heartBeatCheckJob?.cancel()
                            heartBeatCheckJob = null
                            this@WebSocketService.webSocket?.cancel()
                            this@WebSocketService.webSocket = null
                            while (this@WebSocketService.webSocket == null) {
                                ensureActive()
                                initWebSocket()
                                delay(FAILURE_RECONNECT_RATE)
                            }
                        }
                    }
                }
            })
        }
    }

    private suspend fun heartBeatCheck() = coroutineScope {
        delay(HEART_BEAT_RATE)
        while (true) {
            ensureActive()
            webSocket?.send("").let {
                if (it != null && it) {
                    delay(HEART_BEAT_RATE)
                } else {
                    heartBeatCheckJob?.cancel()
                    heartBeatCheckJob = null
                    webSocket?.cancel()
                    webSocket = null
                    initWebSocket()
                }
            }
        }
    }

}